home *** CD-ROM | disk | FTP | other *** search
/ BBS in a Box 3 / BBS in a box - Trilogy III.iso / Files / Prog / U-Z / VideoToolBox Folder / VideoToolboxSources / TestClut.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-03-31  |  14.4 KB  |  472 lines  |  [TEXT/KAHL]

  1. /*
  2. TestClut.c
  3.  
  4. This is used by TimeVideo; I can't think of any reason for including it in
  5. any other program. 
  6.  
  7. OSErr TestClut(FILE *o[2],GDHandle device,short flags,VideoCard *card);
  8.  
  9. Tests whether the video clut can be written and read faithfully. This tests for
  10. bad clut memory, and whether the clut is actually being loaded with what we
  11. sent. A surprisingly large fraction of video drivers fail this test, for various
  12. reasons. The test consists of writing random numbers to all the clut entries,
  13. reading them back, and comparing.
  14.  
  15. The bits of "flags" are tested independently. If flags&testClutQuicklyFlag then
  16. SetEntriesQuickly() will be tested instead of GDSetEntries/GDDirectSetEntries.
  17. If flags&testClutSeriallyFlag then the clut entries will be set individually,
  18. calling GDSetEntries once for each clut entry, to check the clut entry
  19. addressing. If flags&testClutLinearFlag then a simple sequence, will be loaded
  20. into the clut, instead of random numbers, to help figuring out systematic
  21. errors. The sequence is (0,0,0),(1,0,0), (0,2,0),(0,0,3),(4,4,4),(4,0,0), and so
  22. on.
  23.  
  24. Returned value is zero if ok, nonzero if error occurred.
  25.  
  26. TestClut tries to recognize common kinds of driver error and report them
  27. in a sensible way, using the various fields of the card->depth[m].clut structure.
  28. Errors accumulate in the card->depth[m].clut structure, allowing you to make
  29. multiple calls to TestClut and only then summarize the results. It is important
  30. that you zero card->depth[m].clut.tests and card->depth[m].clutQuickly.tests
  31. before your first call to TestClut, to induce it to zero the rest of the
  32. clut structures.
  33.  
  34. Assumes that GDevice record is valid, i.e. the user has not called GDSetMode().
  35. HISTORY:
  36. 3/9/93    dgp    code extracted from the demo TestCluts.c, to create a reusable
  37.         subroutine.
  38. 3/10/93    dgp    No longer assume that GDevice record reflects the actual state of the
  39.         driver.
  40. */
  41. #include "VideoToolbox.h"
  42. #include <Errors.h>
  43. #include "GDInfo.h"
  44.  
  45. // These functions are solely for use within this file.
  46. ColorSpec *MakeClutTable(GDHandle device,short flags,short dacMask);
  47. OSErr WriteClut(GDHandle device,ColorSpec table[],short flags);
  48. int CountClutErrors(GDHandle device,ColorSpec table[],ColorSpec table2[]
  49.     ,short flags,VideoCard *card);
  50. void ReportClutErrors(FILE *file,GDHandle device,ColorSpec table[],ColorSpec table2[]
  51.     ,short flags,VideoCard *card);
  52. OSErr ShowGammaTable(FILE *o[2],GDHandle device);
  53. void RGBToGray(RGBColor *rgb,short dacSize);
  54. Boolean UnequalClutEntry(RGBColor *a,RGBColor *b,short mask);
  55. OSErr VisibleHash(GDHandle device,SetEntriesFunction function,short clutEntries
  56.     ,Boolean *hashPtr);
  57. #define GAMMA_TABLE 0    // Enable only for debugging.
  58.  
  59. OSErr TestClut(FILE *o[2],GDHandle device,short flags,VideoCard *card)
  60. {
  61.     short i,colors,errors;
  62.     OSErr error;
  63.     ColorSpec spec,*table,*table2;
  64.     SetEntriesFunction function;
  65.     VideoCardClutTest *clut;
  66.     
  67.     clut=&card->depth[card->m].clut;
  68.     if(clut->tests==0){
  69.         clut->errors=0;
  70.         clut->color.errors=0;
  71.         clut->gray.errors=0;
  72.         clut->color.zeroStartErrors=0;
  73.         clut->gray.zeroStartErrors=0;
  74.     }
  75.     if(clut->visual.tests==0){
  76.         clut->visual.errors=0;
  77.         clut->visual.zeroStartErrors=0;
  78.     }
  79.     clut=&card->depth[card->m].clutQuickly;
  80.     if(clut->tests==0){
  81.         clut->errors=0;
  82.         clut->color.errors=0;
  83.         clut->gray.errors=0;
  84.         clut->color.zeroStartErrors=0;
  85.         clut->gray.zeroStartErrors=0;
  86.     }
  87.     if(clut->visual.tests==0){
  88.         clut->visual.errors=0;
  89.         clut->visual.zeroStartErrors=0;
  90.     }
  91.     GDInfo(device,card);
  92.     colors=card->depth[card->m].colors;
  93.     #if GAMMA_TABLE
  94.         ShowGammaTable(o,device);
  95.     #endif
  96.     error=GDSaveGamma(device);
  97.     error=GDUncorrectedGamma(device);
  98.     if(error)return error;
  99.     #if GAMMA_TABLE
  100.         ShowGammaTable(o,device);
  101.     #endif
  102.     
  103.     // MAKE TABLE FOR CLUT
  104.     table=MakeClutTable(device,flags,card->dacMask);
  105.     if(table==NULL)return MemError();
  106.     
  107.     // WRITE IT TO CLUT
  108.     error=WriteClut(device,table,flags);
  109.     if(error)return error;
  110.     
  111.     // MAKE BLANK TABLE
  112.     table2=(ColorSpec *)NewPtr(sizeof(*table)*colors);
  113.     if(table2==NULL)return MemError();
  114.     spec.value=spec.rgb.red=spec.rgb.green=spec.rgb.blue=0;
  115.     for(i=0;i<colors;i++)table2[i]=spec;
  116.  
  117.     // READ CLUT INTO BLANK TABLE
  118.     error=GDGetEntries(device,0,colors-1,table2);
  119.     if(!error){
  120.         // COMPARE
  121.         errors=CountClutErrors(device,table,table2,flags,card);
  122.         if(errors)ReportClutErrors(o[1],device,table,table2,flags,card);
  123.         card->clutTest=1;
  124.     }
  125.         
  126.     DisposePtr((Ptr)table);
  127.     DisposePtr((Ptr)table2);
  128.     GDRestoreGamma(device);
  129.     GDRestoreDeviceClut(device);
  130.  
  131. //    if(error || card->visual)return TestClutVisually(device,flags,card);
  132.     return 0;
  133. }
  134.  
  135. OSErr TestClutVisually(GDHandle device,short flags,VideoCard *card)
  136. {
  137.     short i,error,colors;
  138.     char blankLine[]="\r                                      "
  139.         "                                          \r";
  140.     ColorSpec *table,*normalTable;
  141.     Boolean weirdError,normalError;
  142.     VideoCardClutTest *clut;
  143.     
  144.     if(flags&testClutQuicklyFlag)clut=&card->depth[card->m].clutQuickly;
  145.     else clut=&card->depth[card->m].clut;
  146.     if(clut->visual.tests==0){
  147.         clut->visual.errors=0;
  148.         clut->visual.zeroStartErrors=0;
  149.     }
  150.     colors=GDClutSize(device);
  151.     normalTable=((**(**(**device).gdPMap).pmTable)).ctTable;
  152.     if(device==GetMainDevice())
  153.         table=MakeClutTable(device,testClutNegativeFlag,card->dacMask);
  154.     else table=MakeClutTable(device,flags,card->dacMask);
  155.     if(table==NULL)return MemError();
  156.     GDSaveGamma(device);
  157.     GDUncorrectedGamma(device);
  158.     error=WriteClut(device,table,flags);
  159.     if(error){
  160.         GDRestoreGamma(device);
  161.         GDRestoreDeviceClut(device);
  162.         return error;
  163.     }
  164.     printf(blankLine);
  165.     printf("Screen should be weirdly colored; watch for subtle change as you hit return:\r");
  166.     while(getcharUnbuffered()==-1);
  167.     error=WriteClut(device,table,0);
  168.     if(error){
  169.         GDRestoreGamma(device);
  170.         GDRestoreDeviceClut(device);
  171.         return error;
  172.     }
  173.     printf(blankLine);
  174.     printf("Did you see any change at all?");
  175.     weirdError=YesOrNo(0);
  176.     printf("\r");
  177.     error=WriteClut(device,normalTable,flags);
  178.     printf(blankLine);
  179.     printf("Screen should be normal now; watch for subtle change as you hit return:\r");
  180.     while(getcharUnbuffered()==-1);
  181.     error=WriteClut(device,normalTable,0);
  182.     printf(blankLine);
  183.     printf("Did you see any change at all?");
  184.     normalError=YesOrNo(0);
  185.     printf("\r");
  186.     GDRestoreGamma(device);
  187.     GDRestoreDeviceClut(device);
  188.     DisposePtr((Ptr)table);
  189.     clut->visual.tests++;
  190.     clut->visual.errors+=(weirdError || normalError);
  191.     if(!(flags&testClutSeriallyFlag))clut->visual.zeroStartErrors+=(weirdError || normalError);
  192.     return 0;
  193. }
  194.  
  195. ColorSpec *MakeClutTable(GDHandle device,short flags,short dacMask)
  196. {
  197.     short shift,i,colors;
  198.     RGBColor put;
  199.     ColorSpec *table;
  200.     
  201.     colors=GDClutSize(device);
  202.     shift=16-Log2L(colors);
  203.     table=(ColorSpec *)NewPtr(sizeof(*table)*colors);
  204.     if(table==NULL)return table;
  205.     for(i=0;i<colors;i++) {
  206.         if(flags&testClutNegativeFlag){
  207.             put=((**(**(**device).gdPMap).pmTable)).ctTable[colors-1-i].rgb;
  208.         }else if(flags&testClutLinearFlag){
  209.             // Linear test pattern
  210.             put.red=put.green=put.blue=0;
  211.             switch(i%4){
  212.             case 0:
  213.                 put.red=put.green=put.blue=i<<shift;
  214.                 break;
  215.             case 1:
  216.                 put.red=i<<shift;
  217.                 break;
  218.             case 2:
  219.                 put.green=i<<shift;
  220.                 break;
  221.             case 3:
  222.                 put.blue=i<<shift;
  223.                 break;
  224.             }
  225.         }else{
  226.             // Random test pattern
  227.             put.red=randU();
  228.             put.green=randU();
  229.             put.blue=randU();
  230.         }
  231.         put.red&=dacMask;
  232.         put.green&=dacMask;
  233.         put.blue&=dacMask;
  234.         table[i].rgb=put;
  235.     }
  236.     return table;
  237. }
  238.  
  239. Boolean UnequalClutEntry(RGBColor *a,RGBColor *b,short mask)
  240. {
  241.     return (a->red&mask)!=(b->red&mask)
  242.         ||(a->green&mask)!=(b->green&mask)
  243.         ||(a->blue&mask)!=(b->blue&mask);
  244. }
  245.  
  246. int CountClutErrors(GDHandle device,ColorSpec table[],ColorSpec table2[]
  247.     ,short flags,VideoCard *card)
  248. {
  249.     short errors,i,m,colors;
  250.     short colorErrors,grayErrors;
  251.     RGBColor put,gray,got;
  252.     VideoCardClutTest *clut;
  253.     
  254.     m=card->m;
  255.     colors=card->depth[m].colors;
  256.     colorErrors=grayErrors=0;
  257.     if(flags&testClutQuicklyFlag) clut=&card->depth[m].clutQuickly;
  258.     else clut=&card->depth[m].clut;
  259.     for(i=0;i<colors;i++) {
  260.         gray=put=table[i].rgb;
  261.         got=table2[i].rgb;
  262.         RGBToGray(&gray,card->dacSize);
  263.         if(UnequalClutEntry(&put,&got,card->dacMask))colorErrors++;
  264.         if(UnequalClutEntry(&gray,&got,card->dacMask))grayErrors++;
  265.         clut->tests++;
  266.     }
  267.     clut->color.errors+=colorErrors;
  268.     clut->gray.errors+=grayErrors;
  269.     if(!(flags&testClutSeriallyFlag)){
  270.         clut->color.zeroStartErrors+=colorErrors;
  271.         clut->gray.zeroStartErrors+=grayErrors;
  272.     }
  273.     if(card->isGray)errors=grayErrors;
  274.     else errors=colorErrors;
  275.     clut->errors+=errors;
  276.     return errors;
  277. }
  278.  
  279. void ReportClutErrors(FILE *file,GDHandle device,ColorSpec table[],ColorSpec table2[]
  280.     ,short flags,VideoCard *card)
  281. {
  282.     short errors,i,colors=card->depth[card->m].colors;
  283.     short colorError,colorErrors,grayError,grayErrors;
  284.     RGBColor put,gray,got;
  285.     
  286.     // Print one-line error message to file
  287.     fprintf(file,"\n");
  288.     if(flags&testClutQuicklyFlag)fprintf(file,"SetEntriesQuickly != GDGetEntries");
  289.     else switch(card->gdType){
  290.     case fixedType:
  291.         break;
  292.     case clutType:
  293.         fprintf(file,"GDSetEntries != GDGetEntries");
  294.         break;
  295.     case directType:
  296.         fprintf(file,"GDDirectSetEntries != GDGetEntries");
  297.         break;
  298.     }
  299.     if(flags&testClutSeriallyFlag)fprintf(file," (Loaded one clut entry at a time.)\n");
  300.     else fprintf(file," (Loaded whole clut at once.)\n");
  301.  
  302.     // Count errors
  303.     colorErrors=grayErrors=0;
  304.     for(i=0;i<colors;i++) {
  305.         gray=put=table[i].rgb;
  306.         got=table2[i].rgb;
  307.         RGBToGray(&gray,card->dacSize);
  308.         if(UnequalClutEntry(&put,&got,card->dacMask))colorErrors++;
  309.         if(UnequalClutEntry(&gray,&got,card->dacMask))grayErrors++;
  310.     }
  311.     if(colorErrors==0 || grayErrors==0)return;
  312.     
  313.     // Print each error to file
  314.     for(i=0;i<colors;i++) {
  315.         gray=put=table[i].rgb;
  316.         got=table2[i].rgb;
  317.         RGBToGray(&gray,card->dacSize);
  318.         colorError=UnequalClutEntry(&put,&got,card->dacMask);
  319.         grayError=UnequalClutEntry(&gray,&got,card->dacMask);
  320.         if(!card->isGray && colorError || card->isGray && grayError){
  321.             fprintf(file,"Clut[%3d] wrote(0x%04x,0x%04x,0x%04x) "
  322.             "(luminance 0x%04x) but read(0x%04x,0x%04x,0x%04x)\n"
  323.             ,i,put.red,put.green,put.blue,gray.red,got.red,got.green,got.blue);
  324.         }
  325.     }
  326. }
  327.  
  328. OSErr WriteClut(GDHandle device,ColorSpec table[],short flags)
  329. {
  330.     short i,colors=GDClutSize(device);
  331.     char priority=7;
  332.     OSErr error;
  333.     SetEntriesFunction function;
  334.         
  335.     if(flags&testClutQuicklyFlag)function=SetEntriesQuickly;
  336.     else function=GDSetEntriesByType;
  337.     if(flags&testClutSeriallyFlag){
  338.         // Load one clut entry at a time
  339.         for(i=0;i<colors;i++){
  340.             SwapPriority(&priority);    // Force driver to load clut now.
  341.             error=(function)(device,i,0,&table[i]);
  342.             SwapPriority(&priority);    // Force driver to load clut now.
  343.             if(error)return error;
  344.         }
  345.     }else{
  346.         // Load whole clut at once
  347.         SwapPriority(&priority);    // Force driver to load clut now.
  348.         error=(function)(device,0,colors-1,table);
  349.         SwapPriority(&priority);    // Force driver to load clut now.
  350.         if(error)return error;
  351.     }
  352. }
  353.  
  354. OSErr ShowGammaTable(FILE *o[2],GDHandle device)
  355. {
  356.     OSErr error;
  357.     unsigned char *byte;
  358.     unsigned short *word;
  359.     int i,j,identity;
  360.     GammaTbl *gamma;
  361.     
  362.     if((**device).gdType==fixedType)return statusErr;
  363.     error=GDGetGamma(device,&gamma);
  364.     if(error){
  365.         ffprintf(o,"GetGamma: GDGetGamma() error %d\n",error);
  366.         if(error==statusErr)
  367.             ffprintf(o,"The video driver doesn't support this call.\n");
  368.         return error;
  369.     }
  370.     byte=(unsigned char *)gamma->gFormulaData+gamma->gFormulaSize;
  371.     word=(unsigned short *)byte;
  372.     identity=1;
  373.     if(gamma->gDataWidth<=8)
  374.         for(i=0;i<gamma->gDataCnt;i++)identity &= (i==byte[i]);
  375.     else
  376.         for(i=0;i<gamma->gDataCnt;i++)identity &= (i==word[i]);
  377.     if(identity){
  378.         ffprintf(o,"Gamma Table: identity transformation\n");
  379.     }else{
  380.         ffprintf(o,"Gamma Table:\n");
  381.         ffprintf(o,"at 0x%lx,gDataWidth %d,gDataCnt %d,gVersion %d,gType %d,gFormulaSize %d,gChanCnt %d\n"
  382.             ,gamma,gamma->gDataWidth,gamma->gDataCnt,gamma->gVersion,gamma->gType,gamma->gFormulaSize,gamma->gChanCnt);
  383.         for(i=0;i<gamma->gDataCnt;i+=64) {
  384.             ffprintf(o,"%3d: ",i);
  385.             if(gamma->gDataWidth<=8)
  386.                 for(j=0;j<16;j++) ffprintf(o," %3u",byte[i+j]);
  387.             else
  388.                 for(j=0;j<16;j++) ffprintf(o," %3u",word[i+j]);
  389.             ffprintf(o,"\n");
  390.         }
  391.     }
  392.     return 0;
  393. }
  394. /*
  395. When you set a video screen to monochrome or "gray" (as opposed to "color"),
  396. e.g. using the Control Panel:Monitors, the request is passed on to the video
  397. driver. The video driver transforms each of your rgb triplets to a
  398. luminance-equivalent gray, using a formula that must be very similar, if not
  399. equivalent, to the code below. The rounding is bad, e.g. any gray rgb triplet
  400. (i,i,i), other than (0,0,0), is transformed to (i-1,i-1,i-1), which is darker,
  401. failing to preserve luminance. However, my goal was to replicate Apple's crumby
  402. transformation, not to improve it. I presume that the reason that I have to trim
  403. my numbers down a tad (-0.00001) is that I'm doing this with 80-bit precision
  404. whereas the driver uses the 64-bit precision of the SANE routines. Presumably I
  405. could obtain the same result by compiling this subroutine separately, to use
  406. 64-bit floating point, since all the arguments are ints.
  407. */
  408. void RGBToGray(RGBColor *rgb,short dacSize)
  409. // Empirical formula to replicate Apple's luminance mapping.
  410. {
  411.     short i,shift;
  412.     unsigned long n;
  413.     
  414.     shift=16-dacSize;
  415.     i=(rgb->red>>shift)*(0.30-0.00001)+(rgb->green>>shift)*(0.59-0.00001)+(rgb->blue>>shift)*(0.11);
  416.     n=0xffffffffUL/((1<<dacSize)-1);
  417.     rgb->red=rgb->green=rgb->blue=(i*n)>>16;
  418. }
  419.  
  420. OSErr TestClutHash(GDHandle device,SetEntriesFunction function,VideoCardClutTest *clut)
  421. {
  422.     OSErr error;
  423.     
  424.     if(clut->hashTest){
  425.         error=VisibleHash(device,function,0,&clut->hash);
  426.     }else error=0;
  427.     return error;
  428. }
  429.  
  430. OSErr VisibleHash(GDHandle device,SetEntriesFunction function,short clutEntries
  431.     ,Boolean *hashPtr)
  432. {
  433.     OSErr error;
  434.     short mode,clutSize,i;
  435.     Boolean hash;
  436.     long tick;
  437.     ColorSpec *table,*linearTable=NULL;
  438.     char string[100];
  439.  
  440.     clutSize=GDClutSize(device);
  441.     if(clutEntries<0 || clutEntries>clutSize)return 1;
  442.     if(clutEntries==0)clutEntries=clutSize;
  443.     error=GDGetMode(device,&mode,NULL,NULL);
  444.     if(error)mode=(**device).gdMode;
  445.     if(mode>eightBitMode){
  446.         if(function==GDSetEntries)function=GDDirectSetEntries;
  447.         table=linearTable=(ColorSpec *)NewPtr(clutSize*sizeof(linearTable[0]));
  448.         if(linearTable==NULL)return MemError();
  449.         for(i=0;i<clutSize;i++){
  450.             table->rgb.red=table->rgb.green=table->rgb.blue=(long)0xffff*i/(clutSize-1);
  451.             table++;
  452.         }
  453.     }else table=((**(**(**device).gdPMap).pmTable)).ctTable;
  454.     error=(function)(device,0,clutEntries-1,table);
  455.     if(!error){
  456.         printf("                                                                    \r");
  457.         printf("Do you see any hash on the test screen? (No):");
  458.         do{
  459.             tick=TickCount();
  460.             do{
  461.                 (function)(device,0,clutEntries-1,table);
  462.             }while(tick==TickCount());
  463.         }while(!kbhit());
  464.         hash=YesOrNo(0);
  465.         printf("\r");
  466.         if(linearTable!=NULL)DisposePtr((Ptr)linearTable);
  467.         if(hashPtr!=NULL)*hashPtr=hash;
  468.     }
  469.     return error;
  470. }
  471.  
  472.